package org.xcolab.util.http; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; import org.springframework.http.client.ClientHttpRequestInterceptor; import org.springframework.http.client.HttpComponentsClientHttpRequestFactory; import org.springframework.http.converter.StringHttpMessageConverter; import org.springframework.web.client.ResponseErrorHandler; import org.springframework.web.client.RestTemplate; import org.xcolab.util.http.caching.CacheKey; import org.xcolab.util.http.caching.CacheName; import org.xcolab.util.http.caching.provider.CacheProvider; import org.xcolab.util.http.caching.provider.CacheProviderNoOpImpl; import org.xcolab.util.http.exceptions.EntityNotFoundException; import org.xcolab.util.http.exceptions.UncheckedEntityNotFoundException; import org.xcolab.util.http.interceptors.HeaderRequestInterceptor; import org.xcolab.util.http.interceptors.UriAwareResponseInterceptor; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; public class RequestHelper { private final ResponseErrorHandler errorHandler; private RestTemplate restTemplate; private CacheProvider cacheProvider = new CacheProviderNoOpImpl(); public RequestHelper(ResponseErrorHandler errorHandler) { this.errorHandler = errorHandler; restTemplate = new RestTemplate(new HttpComponentsClientHttpRequestFactory()); configureRestTemplate(); } public void setRestTemplate(RestTemplate restTemplate) { this.restTemplate = restTemplate; configureRestTemplate(); } private void configureRestTemplate() { restTemplate.getMessageConverters() .add(0, new StringHttpMessageConverter(Charset.forName("UTF-8"))); restTemplate.setErrorHandler(errorHandler); final List<ClientHttpRequestInterceptor> interceptors = new ArrayList<>(restTemplate.getInterceptors()); interceptors.add(new UriAwareResponseInterceptor()); interceptors.add(new HeaderRequestInterceptor(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE)); restTemplate.setInterceptors(interceptors); } public <R> List<R> getList(UriBuilder uriBuilder, ParameterizedTypeReference<List<R>> typeReference) { return getList(uriBuilder, typeReference, null, CacheName.NONE); } public <T, R> List<R> getList(final UriBuilder uriBuilder, final ParameterizedTypeReference<List<R>> typeReference, final CacheKey<T, List<R>> cacheKey, CacheName cacheName) { return getCached(cacheName, cacheKey, () -> { ResponseEntity<List<R>> response = restTemplate.exchange(uriBuilder.buildString(), HttpMethod.GET, null, typeReference); return response.getBody(); }); } public <T> T get(UriBuilder uriBuilder, Class<T> entityType) throws EntityNotFoundException { return get(uriBuilder, entityType, null, CacheName.NONE); } public <T, R> R get(UriBuilder uriBuilder, Class<R> entityType, CacheKey<T, R> cacheKey, CacheName cacheName) throws EntityNotFoundException { try { return getUnchecked(uriBuilder, entityType, cacheKey, cacheName); } catch (UncheckedEntityNotFoundException e) { throw new EntityNotFoundException(e.getLocalizedMessage()); } } public <R> R getUnchecked(UriBuilder uriBuilder, Class<R> returnType) { return getUnchecked(uriBuilder, returnType, null, CacheName.NONE); } public <T, R> R getUnchecked(final UriBuilder uriBuilder, final Class<R> returnType, CacheKey<T, R> cacheKey, CacheName cacheName) { return getCached(cacheName, cacheKey, () -> restTemplate.getForObject(uriBuilder.buildUri(), returnType)); } public int getCount(UriBuilder uriBuilder) { return getCount(uriBuilder, null, CacheName.MISC_REQUEST); } public int getCount(final UriBuilder uriBuilder, final CacheKey<?, Integer> cacheKey, CacheName cacheName) { return getCached(cacheName, cacheKey, () -> { final HttpHeaders httpHeaders = restTemplate.headForHeaders(uriBuilder.buildString()); final List<String> countHeaders = httpHeaders.get("X-Total-Count"); if (countHeaders.isEmpty()) { return 0; } return Integer.valueOf(countHeaders.get(0)); }); } private <T> T getCached(CacheName cacheName, CacheKey<?, T> cacheKey, Supplier<T> supplier) { T ret; final boolean cacheActive = cacheProvider.isActive() && cacheKey != null && cacheName != CacheName.NONE; if (cacheActive) { ret = cacheProvider.get(cacheKey, cacheName); if (ret != null) { return ret; } } ret = supplier.get(); if (cacheActive) { cacheProvider.add(cacheKey, cacheName, ret); } return ret; } public boolean put(UriBuilder uriBuilder) { return put(uriBuilder, null, null, null); } public <T> boolean put(UriBuilder uriBuilder, T entity) { return put(uriBuilder, entity, null, null); } public <T> boolean put(UriBuilder uriBuilder, T entity, CacheKey<T, T> cacheKey, CacheName cacheName) { final boolean cacheActive = cacheProvider.isActive() && cacheKey != null; if (cacheActive) { cacheProvider.replace(cacheKey, cacheName, entity); } HttpEntity<T> httpEntity = new HttpEntity<>(entity); restTemplate.exchange(uriBuilder.buildString(), HttpMethod.PUT, httpEntity, Void.class); return true; } public boolean delete(UriBuilder uriBuilder) { restTemplate.exchange(uriBuilder.buildString(), HttpMethod.DELETE, null, Void.class); return true; } public <T> boolean delete(UriBuilder uriBuilder, CacheKey<T, T> cacheKey, CacheName cacheName) { restTemplate.exchange(uriBuilder.buildString(), HttpMethod.DELETE, null, Void.class); final boolean cacheActive = cacheProvider.isActive() && cacheKey != null; if (cacheActive) { cacheProvider.delete(cacheKey, cacheName); } return true; } public void deleteFromCache(CacheKey<?, ?> cacheKey, CacheName cacheName) { final boolean cacheActive = cacheProvider.isActive() && cacheKey != null; if (cacheActive) { cacheProvider.delete(cacheKey, cacheName); } } public <T> T post(UriBuilder uriBuilder, Object entity, Class<T> returnType) { return restTemplate.postForObject(uriBuilder.buildString(), entity, returnType); } public void invalidateCache(CacheKey<?, ?> cacheKey, CacheName cacheName) { cacheProvider.delete(cacheKey, cacheName); } public void clearCache() { cacheProvider.clear(); } public void clearCache(CacheName cacheName) { cacheProvider.clear(cacheName); } public void setCacheProvider(CacheProvider cacheProvider) { this.cacheProvider = cacheProvider; } }